package com.gton.person.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.gton.person.entity.Role;
import com.gton.person.entity.Student;
import com.gton.person.mapper.RoleMapper;
import com.gton.person.service.RoleService;
import com.gton.person.service.StudentService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @program: PersonalDesign
 * @description: 权限管理业务处理类
 * @author: GuoTong
 * @create: 2020-11-26 09:35
 **/
@Service
@Slf4j
public class RoleServiceImpl extends ServiceImpl<RoleMapper, Role> implements RoleService {

    @Resource
    private RoleMapper roleMapper;

    /**jackson的JSON转换器*/
    private final ObjectMapper objectMapper = new ObjectMapper();


    /**注入依赖：这里的这个是redis的模板依赖，方便我们java对于redis操作*/
    @Autowired
    private RedisTemplate redisTemplate;


    //真正的用户类的service
    @Autowired
    private StudentService studentService;

    //把数据存入redis
    @Override
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED, readOnly = false)
    public boolean insertRoleByRegister(Role role) {
        /*在数据库里面新增：一条权限；主要是新增的时候给新用户分配权限时调用，或者权限修改的时候调用*/
        int rol = roleMapper.insertRoleByRegister(role);
        /*逻辑判断是否权限添加成功：三元表达式*/
        return rol > 0 ? true : false;
    }

    @Override
    public void loadingNameAndRolesInredis() {
        /*初始化：将全部的用户名和权限存入redis*/
        //第一步：判断redis是否已经存在
        //取出redis中的用户列表
        Object getredis = redisTemplate.opsForValue().get("userNameList");
        if (getredis == null) {
            //如果不存在就加入redis
            List<Student> users = studentService.list();
            //如果数据库查询崩溃：就直接返回
            if (users == null) {
                log.info("数据库崩溃,查询失败");
                return;
            }
            List<Role> roles = roleMapper.selectAllInList();
            //封装数据
            List<Map<String, Set<String>>> list = new ArrayList<>();
            //保证每一个名字都在redis中
            for (Student user : users) {
                //初始化每一个元组：存储的key--用户名，value就是权限列表
                Map<String, Set<String>> map = new HashMap<>();
                //初始化 没有个元组的值：一个名字可能有多个权限，但是权限重复无效，所以用set
                Set<String> value = new HashSet<>();
                //遍历权限列表，找出该人下的所有权限，添加进入set
                for (Role role : roles) {
                    if (user.getId().equals(role.getSid())) {
                        value.add(role.getName());
                    }
                }
                //封装map集合，保存每一个人的名字和对应的权限列表。
                map.put(user.getUsername(), value);
                //封装到总的list集合，存入redis
                list.add(map);
            }
            //把得到的list转为json格式字符串
            String userNameList = null;
            try {
                userNameList = objectMapper.writeValueAsString(list);
            } catch (JsonProcessingException e) {
                e.printStackTrace();
            }
            //redis存入被转为json格式的list集合
            log.info("加载userNameList缓存redis->:" + userNameList);
            redisTemplate.opsForValue().set("userNameList", userNameList);
            //设置过期时间； TimeUnit.MILLISECONDS  毫秒:设置默认时间是SECONDS:秒
            redisTemplate.expire("userNameList", 12, TimeUnit.HOURS);
            return;
        } else {
            //如果缓存中有的话，就更新
            log.info("redis缓存初始化已经完成");
            return;
        }

    }

    //刷新缓存
    @Override
    public void updateRedisForDML() {
        //先设置为空
        redisTemplate.opsForValue().set("userNameList", null);
        //然后重构缓存
        loadingNameAndRolesInredis();
    }

    //把数据从redis取出来
    @Override
    public List<Map<String, Set<String>>> getByRedisNameAndRole(String redisData) {
        Object getRedis = redisTemplate.opsForValue().get(redisData);
        //json转为list
        List<Map<String, Set<String>>> list = null;
        try {
            //先把redis中取出的数据转为字符串，然后转为list
            //使用object.toString()方法把Object转为字符串的话要注意Object不能为null否则会报NullPointException，一般别用这种方法。
            list = objectMapper.readValue(String.valueOf(getRedis), new TypeReference<List<Map<String, Set<String>>>>() {
            });
        } catch (Exception e) {
            e.printStackTrace();
        }
        return list;
    }

    //把数据的名字从redis取出来
    @Override
    public List<String> getByRedisName(String redisData) {
        //得到redis数据库的字段
        List<Map<String, Set<String>>> list = getByRedisNameAndRole(redisData);
        //预封装返回的数据
        List<String> redisSaveName = new ArrayList<>();
        //遍历字段
        for (Map<String, Set<String>> stringSetMap : list) {
            //第一层是list被遍历，里面就是map集合。获取map集合继续遍历map
            Set<Map.Entry<String, Set<String>>> entries = stringSetMap.entrySet();
            //遍历map集合得到key与value
            for (Map.Entry<String, Set<String>> entry : entries) {
                System.out.print(entry.getKey() + ",");
                redisSaveName.add(entry.getKey());
                //map集合的value又是set集合，如果需要继续遍历set集合
                /*Set<String> value = entry.getValue();
                value.forEach(System.out::println);*/
            }
        }
        return redisSaveName;
    }

    //把根据数据的名字从redis取出来权限列表
    @Override
    public Set<String> getByNameRoleList(String userName,String redisData) {
        //得到redis数据库的字段
        List<Map<String, Set<String>>> list = getByRedisNameAndRole(redisData);
        //预封装返回的数据
        Set<String> userNameRoles = new HashSet<>();
        //遍历字段
        for (Map<String, Set<String>> stringSetMap : list) {
            //第一层是list被遍历，里面就是map集合。获取map集合继续遍历map
            Set<Map.Entry<String, Set<String>>> entries = stringSetMap.entrySet();
            //遍历map集合得到key与value
            for (Map.Entry<String, Set<String>> entry : entries) {
                //比较找到该用户对应的key
                if (userName == entry.getKey() || entry.getKey().equalsIgnoreCase(userName)) {
                    //如果key比较正确就找出它的role权限列表
                    Set<String> value = entry.getValue();
                    //把它传递出去：主要是赋值给外部的局部变量实现
                    userNameRoles = value;
                }

            }
        }
        return userNameRoles;
    }
}
